Skip to content

feat: 전체적인 기능 추가#59

Merged
kangsea09 merged 126 commits intomainfrom
develop
Apr 12, 2026
Merged

feat: 전체적인 기능 추가#59
kangsea09 merged 126 commits intomainfrom
develop

Conversation

@kangsea09
Copy link
Copy Markdown
Collaborator

No description provided.

kangsea09 and others added 30 commits April 3, 2026 00:06
fix: 이미지 조회 div 높이 수정 및 css 주석 삭제
feat: 일기 추천 조회 및 목록 조회 추가
feat: 사용자 정보 조회, 달력 조회 연동 추가
fix: 헤더 로그인시 문제 해결
- 회원 정보 불러오기
- 회원 탈퇴(미완)
- 비밀번호 변경(미완)
- 프로필 변경(미완)
- 회원정보 불러오기
- 회원 탈퇴
- 비번 변경
- 프로필 변경
fix: 토큰 만료시 로그아웃 되도록 설정
fix: AI 피드백이 오지 않았을 때 기능 추가
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pages/EditDiary.jsx (1)

88-94: ⚠️ Potential issue | 🟡 Minor

감정 카테고리 아이콘에 cursor: pointer가 있지만 클릭 핸들러가 없습니다.

CategoryImg 스타일(line 192)에 cursor: pointer가 설정되어 있어 클릭 가능해 보이지만, 실제로 onClick 핸들러가 연결되어 있지 않습니다. 사용자가 감정을 수정할 수 없다면 커서 스타일을 제거하고, 수정 가능하다면 핸들러를 구현해야 합니다.

💡 클릭 불가능한 경우 커서 스타일 제거
 const CategoryImg = styled.img`
   width: 40px;
   height: 40px;
-  cursor: pointer;
 `;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/EditDiary.jsx` around lines 88 - 94, Category icons (CategoryImg)
show a pointer cursor but lack click handlers; either remove the cursor from the
CategoryImg styled-component or make them interactive by adding state and
onClick handlers: in EditDiary add a piece of state like selectedEmotion and a
handler function handleSelectEmotion(emotion) that calls setSelectedEmotion,
pass onClick={() => handleSelectEmotion('happy'|'sad'|...)} to each CategoryImg
instance, and update CategoryImg styling to accept a selected prop (e.g.,
props.selected) to render a visible selected style and keep cursor: pointer only
when an onClick/clickable prop is present.
🧹 Nitpick comments (4)
src/pages/Home.jsx (3)

257-269: 사진 목록에서 indexkey로 사용하고 있습니다.

사진이 재정렬되거나 변경될 수 있다면 diaryId 또는 고유 식별자를 key로 사용하는 것이 더 안전합니다.

💡 고유 key 사용 제안
-{recentPhotos.slice(0, 3).map((photo, index) => (
-  <RecentImageWrapper
-    key={index}
+{recentPhotos.slice(0, 3).map((photo) => (
+  <RecentImageWrapper
+    key={photo.diaryId}
     onClick={() => handlePhotoClick(photo.diaryId)}
   >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/Home.jsx` around lines 257 - 269, The list is using the loop index
as the React key which can cause incorrect rendering when items reorder; update
the mapping in the RecentPhotosList to use a stable unique identifier (e.g.,
photo.diaryId) as the key instead of index — locate the map over recentPhotos
(where RecentImageWrapper, RecentImage and handlePhotoClick are used) and change
key from index to the unique id (or a stable fallback property if diaryId may be
absent).

170-171: 날짜 형식 파싱에서 예외 처리가 필요할 수 있습니다.

recommendation.targetDate가 예상과 다른 형식(예: YYYY-MM-DD가 아닌 경우)이면 split("-")parseInt에서 의도치 않은 결과가 발생할 수 있습니다.

💡 방어적 파싱 제안
+const formatRecommendationDate = (dateStr) => {
+  const parts = dateStr?.split("-");
+  if (!parts || parts.length !== 3) return dateStr || "";
+  return `${parts[0]}년 ${parseInt(parts[1], 10)}월 ${parseInt(parts[2], 10)}일`;
+};
+
 const getRecommendationDisplay = () => {
   // ...
-  const dateParts = recommendation.targetDate.split("-");
-  const formattedDate = `${dateParts[0]}년 ${parseInt(dateParts[1], 10)}월 ${parseInt(dateParts[2], 10)}일`;
+  const formattedDate = formatRecommendationDate(recommendation.targetDate);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/Home.jsx` around lines 170 - 171, The date parsing for
recommendation.targetDate is brittle: validate that recommendation.targetDate is
a string matching the expected YYYY-MM-DD pattern before calling split and
parseInt, e.g., check typeof recommendation.targetDate === 'string' and a simple
regex like /^\d{4}-\d{1,2}-\d{1,2}$/; if validation fails, use a safe fallback
(e.g., empty string, null, or a localized "Invalid date" label) for
formattedDate and avoid calling dateParts[?] or parseInt, and ensure dateParts
length is checked (dateParts.length === 3) before constructing formattedDate to
prevent runtime errors in the code that defines dateParts and formattedDate.

56-86: 첫 번째 useEffect에서 비동기 작업 취소 처리가 누락되었습니다.

두 번째 useEffect(lines 88-109)에서는 cancelled 플래그를 사용하여 컴포넌트 언마운트 시 상태 업데이트를 방지하고 있지만, 이 useEffect에서는 동일한 패턴이 적용되지 않았습니다. 컴포넌트가 언마운트된 후 API 응답이 도착하면 메모리 누수 경고가 발생할 수 있습니다.

♻️ 취소 플래그 추가 제안
 useEffect(() => {
+  let cancelled = false;
+
   const fetchData = async () => {
     try {
       const diaryRes = await getDiariesList();
+      if (cancelled) return;
       const diaryData = Array.isArray(diaryRes)
         ? diaryRes
         : (diaryRes?.data ?? []);
       setDiaries(diaryData);
       // ... rest of the code
     } catch (error) {
-      console.error("데이터 로딩 실패:", error);
+      if (!cancelled) {
+        console.error("데이터 로딩 실패:", error);
+      }
     }
   };

   fetchData();
+  return () => {
+    cancelled = true;
+  };
 }, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/Home.jsx` around lines 56 - 86, The first useEffect's async
fetchData (calling getDiariesList, getDiaryRecommendation, getUserSummary) lacks
an unmount cancellation guard; add a local cancelled flag (let cancelled =
false) in the useEffect, check if (!cancelled) before calling state setters
(setDiaries, setTotalDays, setRecentPhotos, setRecommendation, setSummary) after
each awaited response, and return a cleanup function that sets cancelled = true
so no state updates occur after the component unmounts.
src/pages/EditDiary.jsx (1)

104-108: 날짜가 빈 문자열일 경우 파싱 로직에서 문제가 발생할 수 있습니다.

Line 33에서 data.createdAt || ""로 fallback하면 빈 문자열이 될 수 있습니다. 이 경우 "".includes("-")false를 반환하여 빈 문자열이 그대로 렌더링됩니다. 빈 날짜에 대한 처리를 명시적으로 추가하는 것이 좋습니다.

💡 빈 날짜 처리 개선
 <h2>
-  {date.includes("-")
+  {date && date.includes("-")
     ? `${date.split("-")[0]}년 ${parseInt(date.split("-")[1])}월 ${parseInt(date.split("-")[2])}일`
-    : date}
+    : date || "날짜 없음"}
 </h2>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/EditDiary.jsx` around lines 104 - 108, The displayed date rendering
currently assumes `date` may be non-empty and uses `date.includes("-")` which
leaves an empty string rendered when `date` is falsy; update the rendering logic
(the `date` variable coming from `data.createdAt || ""` and the <h2> block) to
explicitly check for an empty/falsy `date` before parsing and render a safe
placeholder (e.g., "날짜 없음" or "—") when missing; when present, keep the existing
hyphen-split parsing (use `date.split("-")` / `parseInt` as before) so the
component never attempts to parse or display an empty string.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/pages/Home.jsx`:
- Around line 78-79: getUserSummary() 응답이 없을 때 summaryRes.data 접근으로 런타임 에러가 발생할
수 있습니다; 수정하려면 Home.jsx의 호출부에서 summaryRes가 falsy인지 확인하거나 optional
chaining/default 값을 사용하여 안전하게 처리하세요 (참조: getUserSummary, summaryRes,
setSummary). 예: 검사 후 setSummary(summaryRes?.data ?? /* 빈 상태 또는 기본값 */) 또는 에러/빈
응답일 때 별도 분기 처리하여 setSummary를 호출하도록 변경하세요.

---

Outside diff comments:
In `@src/pages/EditDiary.jsx`:
- Around line 88-94: Category icons (CategoryImg) show a pointer cursor but lack
click handlers; either remove the cursor from the CategoryImg styled-component
or make them interactive by adding state and onClick handlers: in EditDiary add
a piece of state like selectedEmotion and a handler function
handleSelectEmotion(emotion) that calls setSelectedEmotion, pass onClick={() =>
handleSelectEmotion('happy'|'sad'|...)} to each CategoryImg instance, and update
CategoryImg styling to accept a selected prop (e.g., props.selected) to render a
visible selected style and keep cursor: pointer only when an onClick/clickable
prop is present.

---

Nitpick comments:
In `@src/pages/EditDiary.jsx`:
- Around line 104-108: The displayed date rendering currently assumes `date` may
be non-empty and uses `date.includes("-")` which leaves an empty string rendered
when `date` is falsy; update the rendering logic (the `date` variable coming
from `data.createdAt || ""` and the <h2> block) to explicitly check for an
empty/falsy `date` before parsing and render a safe placeholder (e.g., "날짜 없음"
or "—") when missing; when present, keep the existing hyphen-split parsing (use
`date.split("-")` / `parseInt` as before) so the component never attempts to
parse or display an empty string.

In `@src/pages/Home.jsx`:
- Around line 257-269: The list is using the loop index as the React key which
can cause incorrect rendering when items reorder; update the mapping in the
RecentPhotosList to use a stable unique identifier (e.g., photo.diaryId) as the
key instead of index — locate the map over recentPhotos (where
RecentImageWrapper, RecentImage and handlePhotoClick are used) and change key
from index to the unique id (or a stable fallback property if diaryId may be
absent).
- Around line 170-171: The date parsing for recommendation.targetDate is
brittle: validate that recommendation.targetDate is a string matching the
expected YYYY-MM-DD pattern before calling split and parseInt, e.g., check
typeof recommendation.targetDate === 'string' and a simple regex like
/^\d{4}-\d{1,2}-\d{1,2}$/; if validation fails, use a safe fallback (e.g., empty
string, null, or a localized "Invalid date" label) for formattedDate and avoid
calling dateParts[?] or parseInt, and ensure dateParts length is checked
(dateParts.length === 3) before constructing formattedDate to prevent runtime
errors in the code that defines dateParts and formattedDate.
- Around line 56-86: The first useEffect's async fetchData (calling
getDiariesList, getDiaryRecommendation, getUserSummary) lacks an unmount
cancellation guard; add a local cancelled flag (let cancelled = false) in the
useEffect, check if (!cancelled) before calling state setters (setDiaries,
setTotalDays, setRecentPhotos, setRecommendation, setSummary) after each awaited
response, and return a cleanup function that sets cancelled = true so no state
updates occur after the component unmounts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fa73db24-a6b5-4d73-9ea1-c7065296406e

📥 Commits

Reviewing files that changed from the base of the PR and between af88ce8 and bdb18e7.

📒 Files selected for processing (5)
  • src/apis/photo.api.js
  • src/pages/Diary.jsx
  • src/pages/EditDiary.jsx
  • src/pages/Home.jsx
  • src/pages/PhotoBook.jsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/apis/photo.api.js
  • src/pages/PhotoBook.jsx
  • src/pages/Diary.jsx
📜 Review details
🔇 Additional comments (7)
src/pages/Home.jsx (3)

111-134: 감정 분포 그래프 스타일 계산 로직이 잘 구현되었습니다.

방어적 코딩으로 summaryemotionDistribution 검증을 수행하고, 빈 데이터에 대한 fallback 처리가 적절합니다.


136-158: tileContent 로직이 개선되었습니다.

이전 리뷰 코멘트에서 지적된 날짜 필드 불일치 문제가 해결되었습니다. 현재 tileContentcalendarData API 응답을 사용하고, onClickDaydiaries API 응답을 사용하여 각 데이터 소스에 맞는 적절한 필드를 참조하고 있습니다.


280-320: 추천 일기 조건부 렌더링이 잘 구현되었습니다.

recommendation.diaryId 유무에 따라 추천 버블과 기본 환영 메시지를 적절히 분기 처리하고 있습니다. $clickable transient prop을 사용하여 styled-components에서 DOM 경고를 방지한 점도 좋습니다.

src/pages/EditDiary.jsx (4)

26-49: 데이터 페칭 로직이 잘 구현되었습니다.

isLoading 상태 관리, 에러 처리, finally 블록을 통한 로딩 상태 해제가 적절합니다.


63-75: 이전 리뷰에서 지적된 alert() 문제가 해결되었습니다.

에러 객체를 console.error로 출력하고, 사용자에게는 읽기 쉬운 메시지를 보여주는 패턴이 적절합니다.


19-22: 초기 imageUrls 상태가 테스트 이미지를 포함하고 있습니다.

API 응답에 이미지가 없을 경우(data.imageUrls가 빈 배열) 테스트 이미지가 표시되지 않지만, API 호출이 실패하거나 imageUrls 필드가 없을 경우 Test 이미지가 그대로 유지됩니다. 의도된 동작인지 확인이 필요합니다.


125-152: 이미지 캐러셀 컨트롤이 잘 구현되었습니다.

이미지가 여러 개일 때만 prev/next 버튼을 표시하고, type="button"aria-label을 사용하여 접근성을 고려한 점이 좋습니다.

@kangsea09 kangsea09 merged commit af95637 into main Apr 12, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants